﻿using System;
using System.Collections.Generic;
using System.IO.Ports;
using System.Threading;
using System.Diagnostics;

namespace sconSDK
{
    public class Scon : IDisposable
    {
        const string VERSION_GRT_STR = "&$OV0R02000000xxxDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDxxxxxxxxxx000xxxx0H~";
        const string SCRIPT_RUN_STR = "&$OV0R05000000xxxDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDxxxxxxxxxxxxxx0H~";
        const string SCRIPT_STOP_STR = "&$OV0R00SSSSxxxxxDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDxxxxxxxxxxxxxx0H~";
        const string SCRIPT_PAUSE_STR = "&$OV0R07SSSSxxxxxDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDxxxxxxxxxxxxxx0H~";

        private SerialPort m_comPort = null;

        private bool m_scriptRuning = false;

        public bool ScriptRuning
        {
            get { return m_scriptRuning; }
        }

        private bool m_isConnected = false;
        public bool IsConnected
        {
            get
            {
                return m_isConnected;
            }
        }

        public string PortName
        {
            get
            {
                if (m_isConnected == false)
                {
                    return string.Empty;
                }

                return m_comPort.PortName;
            }
        }

        private int m_version = 0;
        public int Version
        {
            get
            {
                return m_version;
            }
        }

        private int m_currentScriptLine;

        public int CurrentScriptLine
        {
            get { return m_currentScriptLine; }
        }

        private int m_currentIteration;

        public int CurrentIteration
        {
            get { return m_currentIteration; }
        }

        private int m_currentPosition;

        public int CurrentPosition
        {
            get { return m_currentPosition; }
        }

        private int m_stack;

        public int Stack
        {
            get { return m_stack; }
        }

        public bool Connect()
        {
            if (FindAndConnectToScon() == true)
            {

                return true;
            }

            return false;
        }

        private string GetVersion()
        {
            string retVal = WriteAndRead(VERSION_GRT_STR);
            if (string.IsNullOrEmpty(retVal) == true)
            {
                return string.Empty;
            }

            retVal = retVal.Remove(0, 16);
            int i = retVal.IndexOf("DD");
            if (i > -1)
            {
                return retVal.Substring(0, i);
            }

            return string.Empty;
        }

        public void ScriptRun()
        {
            if (m_scriptRuning == false)
            {
                WriteAndRead(SCRIPT_RUN_STR, false);
                m_scriptRuning = true;
            }
        }

        public void ScriptStop()
        {
            if (m_scriptRuning)
            {
                WriteAndRead(SCRIPT_STOP_STR, false);
                m_scriptRuning = false;
            }
        }

        public void ScriptPause()
        {
            if (m_scriptRuning)
            {
                WriteAndRead(SCRIPT_PAUSE_STR, false);
                m_scriptRuning = false;
            }
        }

        private bool FindAndConnectToScon()
        {
            string[] ports = SerialPort.GetPortNames();
            foreach (string port in ports)
            {
                if (TryConnect(port) == true)
                {
                    return true;
                }
            }

            return false;
        }

        private bool TryConnect(string port)
        {
            SerialPort comPort = new SerialPort();
            comPort.BaudRate = 57600;
            comPort.DataBits = 8;
            comPort.StopBits = StopBits.One;
            comPort.Parity = Parity.None;
            comPort.PortName = port;
            comPort.ReadTimeout = 1000;
            comPort.WriteTimeout = 3000;

            if (m_comPort != null)
            {
                m_comPort.Dispose();
                m_comPort = null;
            }
            try
            {
                comPort.Open();
                if (comPort.IsOpen)
                {
                    m_comPort = comPort;
                    comPort.DataReceived += new SerialDataReceivedEventHandler(comPort_DataReceived);
                    ScriptStop();
                    Thread.Sleep(100);
                    string retVal = GetVersion();
                    if (string.IsNullOrEmpty(retVal) == false)
                    {
                        m_version = Convert.ToInt32(retVal);
                        m_isConnected = true;
                        return true;
                    }

                    comPort.Close();
                    comPort.Dispose();
                    m_comPort = null;
                    return false;
                }
            }
            catch (Exception ex)
            {
                if (comPort != null)
                {
                    comPort.Dispose();
                    m_comPort = null;
                }
                return false;
            }

            return false;
        }

        public string WriteAndRead(string data)
        {
            return WriteAndRead(data, true);
        }

        private bool m_isIdle = true;
        private object m_lock = new object();
        private string buffer = string.Empty;
        private string WriteAndRead(string data, bool pause)
        {
            lock (m_lock)
            {
                m_isIdle = false;
                if (pause && m_scriptRuning)
                {
                    ScriptStop();
                }

                bool gotSomeResponse = false;
                buffer = string.Empty;
                string retVal = string.Empty;
                m_comPort.Write(data);
                Stopwatch sw = Stopwatch.StartNew();
                while (true)
                {
                    if ((gotSomeResponse == true &&
                        m_comPort.BytesToRead == 0) ||
                        (gotSomeResponse == false &&
                        sw.ElapsedMilliseconds > 500))
                    {
                        sw.Stop();
                        break;
                    }

                    buffer = m_comPort.ReadExisting();

                    if (string.IsNullOrEmpty(buffer) == false)
                    {
                        retVal += buffer;
                        gotSomeResponse = true;
                    }

                    Thread.Sleep(20);
                }

                m_isIdle = true;

                return retVal;
            }
        }

        void comPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            lock (m_lock)
            {
                if (m_isIdle)
                {
                    string buffer = string.Empty;
                    while (m_comPort != null && m_comPort.IsOpen && m_comPort.BytesToRead > 0)
                    {
                        buffer += m_comPort.ReadExisting();
                        Thread.Sleep(20);
                    }

                    if (buffer.Length == 24)
                    {
                        DebugInfo di = new DebugInfo(buffer);
                        m_currentScriptLine = di.Line;
                        m_currentIteration = di.Loop;
                        m_currentPosition = di.Position;
                        m_stack = di.Stack;
                    }
                }
            }
        }

        #region IDisposable Members

        public void Dispose()
        {
            Dispose(true);
        }

        public void Dispose(bool dispose)
        {
            if (dispose)
            {
                if (m_comPort != null)
                {
                    m_comPort.Dispose();
                    m_comPort = null;
                }
            }
        }

        #endregion
    }
}
